#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2025, Lime Technology
 * Copyright 2012-2025, Bergware International.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Helpers.php";
extract(parse_plugin_cfg('dynamix',true));

// add translations
$_SERVER['REQUEST_URI'] = '';
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";

function dmidecode($key, $n, $all=true) {
  $entries = array_filter(explode($key,shell_exec("dmidecode -qt$n")?:''));
  $properties = [];
  foreach ($entries as $entry) {
    $property = [];
    foreach (explode("\n",$entry) as $line) if (strpos($line,': ')!==false) {
      [$key,$value] = my_explode(': ',trim($line));
      $property[$key] = $value;
    }
    $properties[] = $property;
  }
  return $all ? $properties : $properties[0]??null;
}

function port_get_contents($port) {
  return file_exists($port) ? @file_get_contents($port) : 0;
}

$var      = (array)@parse_ini_file('state/var.ini');
$model    = _var($var,'SYS_MODEL',_('N/A'));
$board    = dmidecode('Base Board Information',2,0);
$bios     = dmidecode('BIOS Information',0,0);
$cpu      = dmidecode('Processor Information',4,0);
$cpumodel = str_ireplace(["Processor","(C)","(R)","(TM)"],["","&#169;","&#174;","&#8482;"],exec("grep -Pom1 '^model name\s+:\s*\K.+' /proc/cpuinfo") ?: $cpu['Version']);
$kernel   = exec("uname -srm");
$openssl  = exec("openssl version|cut -d' ' -f2");

$board['Manufacturer']  = (empty($board['Manufacturer']))  ? _('Unknown') : $board['Manufacturer'];
$board['Product Name']  = (empty($board['Product Name']))  ? "" : $board['Product Name'];
$board['Version']       = (empty($board['Version']))       ? "" : _('Version')." ".$board['Version'];
$board['Serial Number'] = (empty($board['Serial Number'])) ? "" : _('s/n')." ".$board['Serial Number'];
$bios['Vendor']         = (empty($bios['Vendor']))         ? "" : $bios['Vendor'];
$bios['Version']        = (empty($bios['Version']))        ? "" : _('Version')." ".$bios['Version'];
$bios['Release Date']   = (empty($bios['Release Date']))   ? "" : _('Dated')." ".$bios['Release Date'];
$cpu['Current Speed']   = (strpos($cpumodel,'@')===false && !empty($cpu['Current Speed'])) ? " @ {$cpu['Current Speed']}" : "";

// Check for Intel VT-x (vmx) or AMD-V (svm) cpu virtualization support
// If either kvm_intel or kvm_amd are loaded then Intel VT-x (vmx) or AMD-V (svm) cpu virtualization support was found
$strLoadedModules = shell_exec("/etc/rc.d/rc.libvirt test");

// Check for Intel VT-x (vmx) or AMD-V (svm) cpu virtualization support
$strCPUInfo = file_get_contents('/proc/cpuinfo');

if (!empty($strLoadedModules)) {
  // Yah! CPU and motherboard supported and enabled in BIOS
  $hvm = _('Enabled');
} else {
  $hvm = '<a href="https://docs.unraid.net/go/vm-system-preparation/" target="_blank">';
  if (strpos($strCPUInfo,'vmx')===false && strpos($strCPUInfo, 'svm')===false) {
    // CPU doesn't support virtualization
    $hvm .=  _('Not Available');
  } else {
    // Motherboard either doesn't support virtualization or BIOS has it disabled
    $hvm .= _('Disabled');
  }
  $hvm .= '</a>';
}

// Check for any IOMMU Groups
$iommu_groups = shell_exec("find /sys/kernel/iommu_groups/ -type l");

if (!empty($iommu_groups)) {
  // Yah! CPU and motherboard supported and enabled in BIOS
  $iommu = _('Enabled');
} else {
  $iommu = '<a href="https://docs.unraid.net/go/determining-hvmiommu-hardware-support/" target="_blank">';
  if (strpos($strCPUInfo,'vmx')===false && strpos($strCPUInfo, 'svm')===false) {
    // CPU doesn't support virtualization so iommu would be impossible
    $iommu .= _('Not Available');
  } else {
    // Motherboard either doesn't support iommu or BIOS has it disabled
    $iommu .= _('Disabled');
  }
  $iommu .= '</a>';
}

$cache_installed = [];
$cache_devices = dmidecode('Cache Information',7);
foreach ($cache_devices as $device) $cache_installed[] = $device['Socket Designation'].": ".str_replace(['kB','B'],['KB','iB'],$device['Installed Size']);

/*
 Memory Device (16) will get us each ram chip. By matching on MB it'll filter out Flash/Bios chips
 Sum up all the Memory Devices to get the amount of system memory installed. Convert MB to GB
 Physical Memory Array (16) usually one of these for a desktop-class motherboard but higher-end xeon motherboards
 might have two or more of these.  The trick is to filter out any Flash/Bios types by matching on GB
 Sum up all the Physical Memory Arrays to get the motherboard's total memory capacity
 Extract error correction type, if none, do not include additional information in the output
 If maximum < installed then roundup maximum to the next power of 2 size of installed. E.g. 6 -> 8 or 12 -> 16
*/
$sizes = ['MB','GB','TB'];
$memory_type = $ecc = '';
$memory_installed = $memory_maximum = 0;
$memory_devices = dmidecode('Memory Device',17);
$modules = 0;
foreach ($memory_devices as $device) {
  if (empty($device['Type']) || $device['Type']=='Unknown') continue;
  [$size, $unit] = my_explode(' ',$device['Size']);
  $base = array_search($unit,$sizes);
  if ($base!==false) $memory_installed += $size*pow(1024,$base);
  if (!$memory_type) $memory_type = $device['Type'];
  $modules++;
}
$memory = $modules > 1 ? "<span class='link blue-text' onclick=\"$('tr.ram').toggle()\">"._('Memory').":</span>" : _('Memory').':';
$memory_array = dmidecode('Physical Memory Array',16);
foreach ($memory_array as $device) {
  [$size, $unit] = my_explode(' ',$device['Maximum Capacity']);
  $base = array_search($unit,$sizes);
  if ($base>=1) $memory_maximum += $size*pow(1024,$base);
  if (!$ecc && isset($device['Error Correction Type']) && $device['Error Correction Type']!='None') $ecc = $device['Error Correction Type']." ";
}
if ($memory_installed >= 1024) {
  $memory_installed = round($memory_installed/1024);
  $memory_maximum = round($memory_maximum/1024);
  $unit = 'GiB';
} else $unit = 'MiB';

// If maximum < installed then roundup maximum to the next power of 2 size of installed. E.g. 6 -> 8 or 12 -> 16
$low = $memory_maximum < $memory_installed;
if ($low) $memory_maximum = pow(2,ceil(log($memory_installed)/log(2)));
?>
<style>
table.info{margin-top:10px;background-color:inherit}
table.info td:first-child{width:20%;font-weight:bold;padding-left:10px}
tr.ram,tr.port{display:none}
span.link{text-decoration:underline;cursor:pointer}
</style>

<table class='info'>
<tr><td><?=_('Model')?>:</td><td><?=htmlspecialchars($model)?></td></tr>
<tr><td><?=('M/B')?>:</td><td><?="{$board['Manufacturer']} {$board['Product Name']} {$board['Version']} {$board['Serial Number']}"?></td></tr>
<tr><td><?=_('BIOS')?>:</td><td><?="{$bios['Vendor']} {$bios['Version']} {$bios['Release Date']}"?></td></tr>
<tr><td><?=_('CPU')?>:</td><td><?="$cpumodel {$cpu['Current Speed']}"?></td></tr>
<tr><td><?=_('HVM')?>:</td><td><?=$hvm?></td></tr>
<tr><td><?=_('IOMMU')?>:</td><td><?=$iommu?></td></tr>
<tr><td><?=_('Cache')?>:</td><td><?=implode(', ',$cache_installed)?></td></tr>
<tr><td><?=$memory?></td><td><?="$memory_installed $unit $memory_type $ecc("._('max. installable capacity')." $memory_maximum $unit".($low?'*':'')?>)</td></tr>
<?
foreach ($memory_devices as $device) {
  if (empty($device['Type']) || $device['Type']=='Unknown') continue;
  $size = preg_replace('/( .)B$/','$1iB',_var($device,'Size',0));
  echo "<tr class='ram'><td></td><td>",$device['Locator'],": ",_var($device,'Manufacturer')," ",_var($device,'Part Number'),", $size ",_var($device,'Type')," @ ",_var($device,'Configured Memory Speed'),"</td></tr>";
}

exec("ls --indicator-style=none /sys/class/net|grep -Po '^(bond|eth|wlan)\d+$'",$sPorts);
$i = 0;
$network = count($sPorts) > 1 ? "<span class='link blue-text' onclick=\"$('tr.port').toggle()\">"._('Network').":</span>" : _('Network').':';
foreach ($sPorts as $port) {
  $int  = "/sys/class/net/$port";
  $mtu  = port_get_contents("$int/mtu");
  $link = port_get_contents("$int/carrier")==1;
  $name = $i ? "" : $network;
  $more = $i++ ? "port" : "";
  switch (substr($port,0,4)) {
  case 'bond':
    if ($link) {
      $bond_mode = str_replace('Bonding Mode: ','',file("/proc/net/bonding/$port",FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)[1]);
      echo "<tr class='$more'><td>$name</td><td>$port: $bond_mode, mtu $mtu</td></tr>";
    } else {
      echo "<tr class='$more'><td>$name</td><td>$port: ",_('bond down'),"</td></tr>";
    }
    break;
  case 'wlan':
    if ($link) {
      unset($speed);
      exec("iw $port link | awk '/^\s+[rt]x bitrate: /{print $1,$2,$3,$4}'",$speed);
      if (count($speed)==2) {
        [$rxrate, $rxunit] = explode(' ',explode(': ',$speed[0])[1]);
        [$txrate, $txunit] = explode(' ',explode(': ',$speed[1])[1]);
        echo "<tr class='$more'><td>$name</td><td>$port: ",_('Rx'),": ",round($rxrate)," ",str_replace('Bit/s','bps',$rxunit),", ",_('Tx').": ",round($txrate)," ",str_replace('Bit/s','bps',$txunit),", mtu $mtu</td></tr>";
      } else {
        echo "<tr class='$more'><td>$name</td><td>$port: ",_('not connected'),"</td></tr>";
      }
    } else {
      echo "<tr class='$more'><td>$name</td><td>$port: ",_('interface down'),"</td></tr>";
    }
    break;
  default:
    if ($link) {
      $speed = file_get_contents("$int/speed");
      $duplex = file_get_contents("$int/duplex");
      echo "<tr class='$more'><td>$name</td><td>$port: $speed Mbps, $duplex duplex, mtu $mtu</td></tr>";
    } else {
      echo "<tr class='$more'><td>$name</td><td>$port: ",_('interface down'),"</td></tr>";
    }
    break;
  }
}
?>
<tr><td><?=_('Kernel')?>:</td><td><?=$kernel?></td></tr>
<tr><td><?=_('OpenSSL')?>:</td><td><?=$openssl?></td></tr>
<tr><td><?=_('Uptime')?>:</td><td><span class='uptime'></span></td></tr>
</table>
